package org.jetlinks.pro.rule.engine;

import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.hswebframework.web.authorization.token.DefaultUserTokenManager;
import org.hswebframework.web.authorization.token.UserTokenManager;
import org.hswebframework.web.starter.jackson.CustomCodecsAutoConfiguration;
import org.jetlinks.core.cluster.ClusterManager;
import org.jetlinks.core.device.DeviceRegistry;
import org.jetlinks.core.event.EventBus;
import org.jetlinks.core.ipc.IpcService;
import org.jetlinks.core.rpc.RpcServiceFactory;
import org.jetlinks.core.server.monitor.GatewayServerMetrics;
import org.jetlinks.core.server.monitor.GatewayServerMonitor;
import org.jetlinks.pro.gateway.session.DefaultDeviceSessionManager;
import org.jetlinks.pro.gateway.session.entity.PersistentSessionEntity;
import org.jetlinks.pro.protocol.SpringServiceContext;
import org.jetlinks.pro.rule.engine.configuration.RuleEngineConfiguration;
import org.jetlinks.rule.engine.api.model.RuleModel;
import org.jetlinks.rule.engine.api.model.RuleNodeModel;
import org.jetlinks.rule.engine.api.task.ExecutionContext;
import org.jetlinks.rule.engine.api.task.TaskExecutor;
import org.jetlinks.rule.engine.api.task.TaskExecutorProvider;
import org.jetlinks.rule.engine.cluster.ClusterSchedulerRegistry;
import org.jetlinks.rule.engine.defaults.LambdaTaskExecutor;
import org.jetlinks.rule.engine.defaults.LocalScheduler;
import org.jetlinks.rule.engine.defaults.LocalWorker;
import org.jetlinks.rule.engine.model.RuleModelParserStrategy;
import org.jetlinks.supports.cluster.EventBusDeviceOperationBroker;
import org.jetlinks.supports.cluster.redis.RedisClusterManager;
import org.jetlinks.supports.event.BrokerEventBus;
import org.jetlinks.supports.ipc.EventBusIpcService;
import org.jetlinks.supports.protocol.management.ClusterProtocolSupportManager;
import org.jetlinks.supports.rpc.IpcRpcServiceFactory;
import org.jetlinks.supports.server.monitor.MicrometerGatewayServerMetrics;
import org.jetlinks.supports.test.InMemoryDeviceRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;

import java.time.Duration;
import java.util.concurrent.ThreadLocalRandom;

@Configuration
@ImportAutoConfiguration({
    CustomCodecsAutoConfiguration.class,
    RuleEngineConfiguration.class
})
public class RuleEngineTestConfiguration {

    @Bean
    public DeviceRegistry deviceRegistry() {
        return new InMemoryDeviceRegistry();
    }

    @Bean
    public ClusterManager clusterManager(ReactiveRedisTemplate<Object, Object> template) {
        return new RedisClusterManager("test", "test", template);
    }

    @Bean
    public EventBus eventBus() {
        return new BrokerEventBus();
    }

    @Bean
    public ClusterProtocolSupportManager clusterProtocolSupportManager(ClusterManager clusterManager) {
        return new ClusterProtocolSupportManager(clusterManager);
    }

    @Bean
    public SpringServiceContext springServiceContext(ApplicationContext context) {
        return new SpringServiceContext(context);
    }

    @Bean
    public Scheduler scheduler() {
        return Schedulers.parallel();
    }

    @Bean(initMethod = "start", destroyMethod = "dispose")
    public EventBusDeviceOperationBroker clusterDeviceOperationBroker(ClusterManager clusterManager, EventBus eventBus) {
        return new EventBusDeviceOperationBroker(clusterManager.getCurrentServerId(), eventBus);
    }

    @Bean
    public UserTokenManager userTokenManager() {
        return new DefaultUserTokenManager();
    }

    @Bean(initMethod = "init", destroyMethod = "shutdown")
    public DefaultDeviceSessionManager deviceSessionManager(ReactiveRepository<PersistentSessionEntity, String> repository,
                                                            DeviceRegistry registry) {
        DefaultDeviceSessionManager sessionManager = new DefaultDeviceSessionManager();
        sessionManager.setGatewayServerMonitor(new GatewayServerMonitor() {
            @Override
            public String getCurrentServerId() {
                return "test";
            }

            @Override
            public GatewayServerMetrics metrics() {
                return new MicrometerGatewayServerMetrics("test");
            }
        });
        sessionManager.setRegistry(registry);
        sessionManager.setSessionRepository(repository);

        return sessionManager;
    }

    @Bean
    public IpcService rpcService(EventBus eventBus) {
        return new EventBusIpcService(ThreadLocalRandom.current().nextInt(100000, 1000000), eventBus);
    }

    @Bean
    public RpcServiceFactory rpcServiceFactory(IpcService ipcService) {
        return new IpcRpcServiceFactory(ipcService);
    }

    @Bean
    public TaskExecutorProvider testTaskExecutorProvider() {

        return new TaskExecutorProvider() {
            @Override
            public String getExecutor() {
                return "test";
            }

            @Override
            public Mono<TaskExecutor> createTask(ExecutionContext context) {
                return Mono.just(new LambdaTaskExecutor("test", context, data -> {
                    return Mono.just(context.newRuleData(data));
                }));
            }
        };
    }

    @Bean
    public RuleModelParserStrategy ruleModelParserStrategy() {
        return new RuleModelParserStrategy() {
            @Override
            public String getFormat() {
                return "test";
            }

            @Override
            public RuleModel parse(String modelDefineString) {
                RuleModel model = new RuleModel();
                RuleNodeModel ruleNodeModel = new RuleNodeModel();
                ruleNodeModel.setId("test");
                ruleNodeModel.setName("Test");
                ruleNodeModel.setExecutor("test");
                model.getNodes()
                     .add(ruleNodeModel);

                return model;
            }
        };
    }

//    @Bean(initMethod = "setup", destroyMethod = "cleanup")
//    public ClusterSchedulerRegistry schedulerRegistry(EventBus eventBus,
//                                                      RpcServiceFactory rpcService,
//                                                      ObjectProvider<TaskExecutorProvider> taskExecutorProviders) {
//        ClusterSchedulerRegistry registry = new ClusterSchedulerRegistry(eventBus, rpcService);
//        LocalScheduler scheduler = new LocalScheduler("test");
//        LocalWorker worker=  new LocalWorker("test", "test", eventBus, (condition, context) -> true);
//        taskExecutorProviders.forEach(worker::addExecutor);
//        scheduler.addWorker(worker);
//        registry.register(scheduler);
//        registry.setKeepaliveInterval(Duration.ofSeconds(30));
//        return registry;
//    }


}
